﻿using Sustainalytics.ClientDW.DataAccessLayer.ESGRatings;
using Sustainalytics.DataLayer;
using Sustainalytics.DataLayer.EsgRatings;
using Sustainalytics.DataLayer.Shared;
using Sustainalytics.Entities;
using Sustainalytics.Entities.ESGRatings;
using Sustainalytics.EsgRatings.BusinessLogic;
using Sustainalytics.Synchronization.Interface;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;

namespace Sustainalytics.ESGRatings.BusinessLogic
{
    public class ESGRatingsCompanyWeightsSyncStep : ISyncStep
    {
        public SyncStepOrder ExecutionOrder => SyncStepOrder.CompanyWeights;

        public SyncContext Start(SyncContext syncContext)
        {
            try
            {
                syncContext.Token.ThrowIfCancellationRequested();
                var clientPlatformConnectionString = syncContext.GAConnectionStrings["ClientPlatform"];
                var clientDwConnectionString = syncContext.GAConnectionStrings["ClientDW"];
                syncContext.Log("IClientDwRepository acquired based on connection string " + clientDwConnectionString);

                var esgInputFileRepository = EsgInputFileRepository.GetInstance(new FileRepository(clientPlatformConnectionString),
                                                                                new RepositoryCollection<EsgInputFile>(clientPlatformConnectionString));
                syncContext.Log("IEsgInputFileRepository acquired based on connection string " + clientPlatformConnectionString);

                var weightMatricesRepository = WeightMatricesRepository.GetInstance(clientDwConnectionString);
                syncContext.Log("IWeightMatricesRepository acquired based on connection string " + clientDwConnectionString);

                var esgProfileRepository = new RepositoryCollection<Entities.ESGRatings.ESGProfile>(clientPlatformConnectionString);
                syncContext.Log("RepositoryCollection<Sustainalytics.Entities.ESGProfile> acquired based on connection string " + clientPlatformConnectionString);

                var accountRepository = new RepositoryCollection<Account>(clientPlatformConnectionString);
                syncContext.Log("RepositoryCollection<Sustainalytics.Entities.Account> acquired based on connection string " + clientPlatformConnectionString);

                var latestUnprocessedFiles = esgInputFileRepository.ListLatestUnprocessedFiles(EsgInputFileType.WeightMatrix);

                var _clientDwRepository = ResearchProductsDataReader.GetInstance(clientDwConnectionString);

                var rpIndicatorsNumbers = _clientDwRepository.ListIndicatorTemplatesNumbers().Result;
                var rootPeerGroups = _clientDwRepository.ListPeerGroupsNames().Result.ToDictionary(pg => pg, pg => pg, StringComparer.InvariantCultureIgnoreCase);
                var companyTypes = _clientDwRepository.ListCompanyTypes().Result.ToDictionary(ct => ct, ct => ct, StringComparer.InvariantCultureIgnoreCase);
                var templateTypes = _clientDwRepository.ListTemplateTypes().Result.ToDictionary(tt => tt, tt => tt, StringComparer.InvariantCultureIgnoreCase);
                var companiesIds = _clientDwRepository.ListCompaniesIds(true).Result;
                var corporateData = _clientDwRepository.ListCompaniesCorporateData(companiesIds.Values).Result;
                var weightMatricesProcessor = new WeightMatricesProcessor(weightMatricesRepository);
                var weightMatricesManagement = WeightMatricesManagement.GetInstance(esgInputFileRepository, weightMatricesRepository, esgProfileRepository, accountRepository);

                syncContext.Log("IWeightMatricesManagement acquired");

                syncContext.Token.ThrowIfCancellationRequested();
                Stopwatch watch = Stopwatch.StartNew();

                weightMatricesProcessor.ProcessLoadedWeightMatrices(
                    latestUnprocessedFiles,
                    rpIndicatorsNumbers,
                    rootPeerGroups,
                    companyTypes,
                    templateTypes,
                    corporateData.ToDictionary(x => x.CapitalIqId, x => x.Id))
                    .ForEach(ale =>
                    {
                        string errors = string.Empty;
                        string activities = string.Empty;

                        if (ale.Log.Errors.Any())
                            errors = ale.Log.Errors.Aggregate((i, j) => i + Environment.NewLine + j);

                        if (ale.Log.Activity.Any())
                            activities = ale.Log.Activity.Aggregate((i, j) => i + Environment.NewLine + j);

                        AddChangedElementToSyncContext(syncContext, ale.ProcessedFile, errors);
                        syncContext.Log(errors + Environment.NewLine + activities);
                    });

                weightMatricesProcessor.EnsureWeightsConsistencyOnCompanies(corporateData).ForEach(ale =>
                {
                    string errors = string.Empty;
                    string activities = string.Empty;

                    if (ale.Errors.Any())
                        errors = ale.Errors.Aggregate((i, j) => i + Environment.NewLine + j);

                    if (ale.Activity.Any())
                        activities = ale.Activity.Aggregate((i, j) => i + Environment.NewLine + j);

                    syncContext.Log(errors + Environment.NewLine + activities);
                });

                weightMatricesManagement.DeleteCustomWeightMatrices().ForEach(ale => syncContext.Log(ale));
                watch.Stop();

                syncContext.Log(String.Format("ESGRatingsCompanyWeightsSyncStep took {0} ms", watch.ElapsedMilliseconds));
            }
            catch (OperationCanceledException)
            {
                throw;
            }
            catch (Exception ex)
            {
                syncContext.Log(ex.ToString());
                syncContext.Status = SyncOperationStatus.Failed;
                throw;
            }

            return syncContext;
        }

        private void AddChangedElementToSyncContext(SyncContext syncContext, EsgInputFileComplete weightMatrixfile, string errorsLog)
        {
            if (syncContext == null) return;

            var changedElement = new ChangedElement()
            {
                AffectedObjectName = ChangedElement.AFFECTED_INPUT_FILE_NAME_OBJECT,
                AffectedCollection = weightMatrixfile.GetType().Name,
                AffectedDocumentType = weightMatrixfile.GetType(),
                AffectedDocument = weightMatrixfile,
                AffectedDocumentId = weightMatrixfile.Id,
                AffectedDB = syncContext.GAConnectionStrings["ClientPlatform"],
                Status = errorsLog.Length > 0 ? SyncResultStatus.AppliedFailure : SyncResultStatus.AppliedSuccess,
                Detail = errorsLog.Length > 0 ? new Dictionary<string, object>() { { "ErrorsLog", errorsLog } } : new Dictionary<string, object>()
            };

            syncContext.AddChange(changedElement);
        }
    }
}
